﻿using System.Collections.Generic;

namespace Scenario_Editor.terrain
{
    internal class PerlinNoise2D
    {
        #region Private Fields

        private static List<long> FirstPrimeNumber;
        private static List<long> SecondPrimeNumber;
        private static List<long> ThirdPrimeNumber;

        #endregion Private Fields

        #region Public Methods

        public static float GetValue2DCubic(int x, int y, float Frequenz, float Amplitude, float Persistence, float Octaves)
        {
            float value = 0;
            Frequenz = 1 / Frequenz;
            float[][] v = new float[4][];
            for (int i = 0; i < 4; i++)
                v[i] = new float[4];
            for (int i = 0; i < Octaves; i++)
            {
                v[0][3] = Noise2D((int)(x * Frequenz) - 1, (int)(y * Frequenz) + 2, i);
                v[0][2] = Noise2D((int)(x * Frequenz), (int)(y * Frequenz) + 2, i);
                v[0][1] = Noise2D((int)(x * Frequenz) + 1, (int)(y * Frequenz) + 2, i);
                v[0][0] = Noise2D((int)(x * Frequenz) + 2, (int)(y * Frequenz) + 2, i);

                v[3][3] = Noise2D((int)(x * Frequenz) - 1, (int)(y * Frequenz) - 1, i);
                v[3][2] = Noise2D((int)(x * Frequenz), (int)(y * Frequenz) - 1, i);
                v[3][1] = Noise2D((int)(x * Frequenz) + 1, (int)(y * Frequenz) - 1, i);
                v[3][0] = Noise2D((int)(x * Frequenz) + 2, (int)(y * Frequenz) - 1, i);

                v[1][3] = Noise2D((int)(x * Frequenz) - 1, (int)(y * Frequenz) + 1, i);
                v[1][2] = Noise2D((int)(x * Frequenz), (int)(y * Frequenz) + 1, i);
                v[1][1] = Noise2D((int)(x * Frequenz) + 1, (int)(y * Frequenz) + 1, i);
                v[1][0] = Noise2D((int)(x * Frequenz) + 2, (int)(y * Frequenz) + 1, i);

                v[2][3] = Noise2D((int)(x * Frequenz) - 1, (int)(y * Frequenz), i);
                v[2][2] = Noise2D((int)(x * Frequenz), (int)(y * Frequenz), i);
                v[2][1] = Noise2D((int)(x * Frequenz) + 1, (int)(y * Frequenz), i);
                v[2][0] = Noise2D((int)(x * Frequenz) + 2, (int)(y * Frequenz), i);

                value += Cubic_Interpolate_2D(v[0][3], v[0][2], v[0][1], v[0][0], v[1][3], v[1][2], v[1][1], v[1][0], v[2][3], v[2][2], v[2][1], v[2][0], v[3][3], v[3][2], v[3][1], v[3][0], x % (1 / Frequenz) * Frequenz, y % (1 / Frequenz) * Frequenz) * Amplitude;
                Frequenz *= 2;
                Amplitude *= Persistence;
            }
            return value * 0.01f;
        }

        public static void SetupNoise()
        {
            FirstPrimeNumber = new List<long>();
            SecondPrimeNumber = new List<long>();
            ThirdPrimeNumber = new List<long>();

            FirstPrimeNumber.Add(15731);
            FirstPrimeNumber.Add(15733);
            FirstPrimeNumber.Add(15737);
            FirstPrimeNumber.Add(15739);
            FirstPrimeNumber.Add(15749);
            FirstPrimeNumber.Add(15761);
            FirstPrimeNumber.Add(15767);
            FirstPrimeNumber.Add(15773);
            FirstPrimeNumber.Add(15787);
            FirstPrimeNumber.Add(15791);
            FirstPrimeNumber.Add(15797);
            FirstPrimeNumber.Add(15803);
            FirstPrimeNumber.Add(15809);
            FirstPrimeNumber.Add(15817);
            FirstPrimeNumber.Add(15823);

            SecondPrimeNumber.Add(789221);
            SecondPrimeNumber.Add(789227);
            SecondPrimeNumber.Add(789251);
            SecondPrimeNumber.Add(789311);
            SecondPrimeNumber.Add(789323);
            SecondPrimeNumber.Add(789331);
            SecondPrimeNumber.Add(789343);
            SecondPrimeNumber.Add(789367);
            SecondPrimeNumber.Add(789377);
            SecondPrimeNumber.Add(789389);
            SecondPrimeNumber.Add(789391);
            SecondPrimeNumber.Add(789407);
            SecondPrimeNumber.Add(789419);
            SecondPrimeNumber.Add(789443);
            SecondPrimeNumber.Add(789473);

            ThirdPrimeNumber.Add(1376312627);
            ThirdPrimeNumber.Add(1376312629);
            ThirdPrimeNumber.Add(1376312657);
            ThirdPrimeNumber.Add(1376312687);
            ThirdPrimeNumber.Add(1376312689);
            ThirdPrimeNumber.Add(1376312753);
            ThirdPrimeNumber.Add(1376312783);
            ThirdPrimeNumber.Add(1376312789);
            ThirdPrimeNumber.Add(1376312813);
            ThirdPrimeNumber.Add(1376312857);
            ThirdPrimeNumber.Add(1376312879);
            ThirdPrimeNumber.Add(1376312881);
            ThirdPrimeNumber.Add(1376312897);
            ThirdPrimeNumber.Add(1376312909);
            ThirdPrimeNumber.Add(1376312929);
        }

        #endregion Public Methods

        #region Private Methods

        private static float Cubic_Interpolate(float v0, float v1, float v2, float v3, float x)
        {
            float P = (v3 - v2) - (v0 - v1);
            float Q = (v0 - v1) - P;
            float R = v2 - v0;
            float S = v1;

            return P * x * x * x + Q * x * x + R * x + S;
        }

        private static float Cubic_Interpolate_2D(float v_12, float v02, float v12, float v22, float v_11, float v01, float v11, float v21, float v_10, float v00, float v10, float v20, float v_1_1, float v0_1, float v1_1, float v2_1, float x, float y) // _ stet für '-', da das Minus im Variabelnamen nicht erlaubt ist
        {
            float x_1 = Cubic_Interpolate(v_1_1, v_10, v_11, v_12, y);      // Einfluss auf die x-1 Ebene
            float x0 = Cubic_Interpolate(v0_1, v00, v01, v02, y);           // Einfluss auf die x0 Ebene
            float x1 = Cubic_Interpolate(v1_1, v10, v11, v12, y);           // Einfluss auf die x1 Ebene
            float x2 = Cubic_Interpolate(v2_1, v20, v21, v22, y);           // Einfluss auf die x2 Ebene
            float result = Cubic_Interpolate(x_1, x0, x1, x2, x);           // Interpolation der x-1, x0, x1 und x2 Ebene

            return result;
        }

        private static float Noise1D(int n, int octave)
        {
            //n = ((n >> 13) ^ n);
            var test0 = (n * n * FirstPrimeNumber[octave] + SecondPrimeNumber[octave]);
            var test1 = (n * test0 + ThirdPrimeNumber[octave]);
            var test2 = (test1 & 0x7fffffff);
            var test3 = test2 / 1073741824.0f;
            return (1.0f - test3);
        }

        //static float Noise(int n, int octave)
        //{
        //    n = (n << 13) ^ n;

        //    return (1.0f - ((n * (n * n * FirstPrimeNumber[octave] + SecondPrimeNumber[octave]) + ThirdPrimeNumber[octave]) & 0x7fffffff) / 1073741824.0f);
        //}
        //static float Noise1D(int n)
        //{
        //    n = (n << 13) ^ n;

        //    return (1.0f - ((n * (n * n * 15731 + 789221) + 1376312627) & 0x7fffffff) / 1073741824.0f);
        //}
        //        static float Noise1D(int n, int octave)
        //        {
        ////n := x + y * 57 + Seed;
        ////n := ( n shl 13 ) xor n;
        ////rand := 1 - ( (n * (n * n * 15731 + 789221) + 1376312589) and $7fffffff) / 1073741824;

        //            Random r = new Random();
        //            return r.Next(0, 1000) / 1000.0f;
        //             n = (n << 13) ^ n;
        //            return 1.0f - (float)( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f;
        //            //int test  = ((int)(n * (n * n * FirstPrimeNumber[octave] + SecondPrimeNumber[octave]) + ThirdPrimeNumber[octave]) );
        //            //return (float)1.0f -( test & 0x7fffffff) / 1073741824.0f;
        //        }
        private static float Noise2D(int x, int y, int octave)
        {
            return Noise1D(x + y, octave);
            //return Noise1D(x  + y );//, octave);
        }

        #endregion Private Methods
    }
}